package com.yzs.exam.utils;

import com.yzs.exam.entity.Question;
import com.yzs.exam.entity.enums.ExamPaperWeightEnum;
import com.yzs.exam.entity.exam.ExamPaperAlgotithmBean;
import com.yzs.exam.entity.exam.Population;
import com.yzs.exam.entity.exam.Rule;
import com.yzs.exam.service.QuestionService;
import org.springframework.beans.factory.annotation.Autowired;

import java.util.ArrayList;
import java.util.List;

/**
 * 遗传算法
 * @author yzs
 * @create 2021-04-30 14:52
 */
public class GAUtil {

    //编译概率
    private static final double mutationRate = 0.085;

    //精英主义
    private static final boolean elitism = true;

    //淘汰数组大小
    private static final int tournamentSize = 5;



    /**
     * 种群进化
     * @param pop 种群对象
     * @param rule 进化规则
     * @return
     */
    public static Population evolvePopulation(Population pop, Rule rule,QuestionService questionService){

        Population newPop = new Population(pop.getLength());
        int elitismOffset;
        //精英主义
        if(elitism){
            elitismOffset = 1;
            //保留上一代最优秀的个体
            ExamPaperAlgotithmBean fitness = pop.getFitness();
            fitness.setId(0);
            newPop.setPaper(0,fitness);
        }

        //种群交叉操作，从当前的种群pop来创建下一代种群newPop
        for (int i = elitismOffset; i < newPop.getLength(); i++){
            //得到两个较优选择
            ExamPaperAlgotithmBean parent1 = select(pop);
            ExamPaperAlgotithmBean parent2 = select(pop);
            //保持连个选择不同
            while (parent1.getId() == parent2.getId()){
                parent2 = select(pop);
            }
            //交叉
            ExamPaperAlgotithmBean child = crossover(parent1, parent2, rule,questionService);
            child.setId(i);
            newPop.setPaper(i,child);
        }

        //种群变异操作
        ExamPaperAlgotithmBean tmpPeper;
        for (int i = elitismOffset; i < newPop.getLength(); i++){
            tmpPeper = newPop.getPaper(i);
            mutate(tmpPeper,questionService);
            //计算知识点覆盖率和适应度
            tmpPeper.setChapterCoverage(rule);
            tmpPeper.setAdaptationDegree(rule, ExamPaperWeightEnum.CHAPTER_WEIGHT,ExamPaperWeightEnum.DIFFICULTY_WEIGHT);
        }
        return newPop;
    }

    /**
     * 选择算子:得到最优个体
     * @param population
     * @return
     */
    public static ExamPaperAlgotithmBean select(Population population){
        Population pop = new Population(tournamentSize);
        for (int i = 0; i < tournamentSize; i++){
            pop.setPaper(i,population.getPaper((int)(Math.random()*population.getLength())));
        }
        return pop.getFitness();
    }

    /**
     * 交叉算子：
     * @param parent1
     * @param parent2
     * @param rule
     * @return
     */
    public static ExamPaperAlgotithmBean crossover(ExamPaperAlgotithmBean parent1,ExamPaperAlgotithmBean parent2,Rule rule,QuestionService questionService){
        ExamPaperAlgotithmBean child = new ExamPaperAlgotithmBean(parent1.getQuestionSize());
        int s1 = (int) Math.random() * parent1.getQuestionSize();
        int s2 = (int) Math.random() * parent1.getQuestionSize();
        // parent1的startPos、endPos之间的序列，会被遗传到下一代
        int startPos = s1 < s2 ? s1 : s2;
        int endPos = s1 > s2 ? s1 : s2;
        for (int i = startPos; i < endPos; i++){
            child.saveQuestion(i,parent1.getQuestions().get(i));//parent1遗传给下一代的序列
        }
        List<Integer> chapterList = rule.getChapters();
        for (int i = 0; i < startPos; i++){
            if (!child.containsQuestion(parent2.getQuestions().get(i))){
                child.saveQuestion(i,parent2.getQuestions().get(i));
            }else {
                //如果出现相同题目，重新查找一题题目类型、知识点相同的题目
                Integer type = parent2.getQuestions().get(i).getQuestionType();
                List<Question> questions = questionService.selectByLevelTypeChapters(type, chapterList);
                child.saveQuestion(i,questions.get((int) Math.random()*questions.size()));
            }
        }
        for (int i = endPos; i < parent2.getQuestionSize(); i++){
            if (!child.containsQuestion(parent2.getQuestions().get(i))){
                child.saveQuestion(i,parent2.getQuestions().get(i));
            }else {
                //如果出现相同题目，重新查找一题题目类型、知识点相同的题目
                Integer type = parent2.getQuestions().get(i).getQuestionType();
                List<Question> questions = questionService.selectByLevelTypeChapters(type, chapterList);
                child.saveQuestion(i,questions.get((int) Math.random()*questions.size()));
            }
        }
        return child;
    }

    /**
     * 突变算子 每个个体的每个基因都有可能突变 每个基因变异的概率大概为0.085，小于等于时可以变异
     * @param paper
     */
    public static void mutate(ExamPaperAlgotithmBean paper,QuestionService questionService){
        Question tmpQuestion;
        List<Question> list;
        int index;
        for (int i = 0;i < paper.getQuestionSize(); i++){
            if (Math.random() <= mutationRate){
                //进行突变
                tmpQuestion = paper.getQuestions().get(i);
                //从题库中获取和变异的题目类型一样分数相同的的题目（不包含编译题目）
                Integer type = paper.getQuestions().get(i).getQuestionType();
                Integer chapterId = paper.getQuestions().get(i).getChapterId();
                List<Integer> chapterIds = new ArrayList<>();
                chapterIds.add(chapterId);
                list = questionService.selectByLevelTypeChapters(type, chapterIds);
                if (list.size() > 0){
                    index = (int) Math.random()*list.size();
                    paper.saveQuestion(i,list.get(index));
                }

            }
        }
    }

}
